/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2021-12-10     xu       the first version
 */
#include "amap_weather_api.h"
#include "amap_location_api.h"
#include "cJSON.h"
#include "gbk2utf8.h"
#include "http_api_utils.h"
#include "webclient.h"

#define DBG_TAG "wea.api"
#define DBG_LVL DBG_LOG
#include <ctype.h>
#include <rtdbg.h>
#include <rtthread.h>
#include <stdlib.h>

#define WEATHER_URL_BUFFER_SIZE 128
#define WEATHER_URL_FORMAT "http://restapi.amap.com/v3/weather/weatherInfo?key=%s&city=%s&extensions=%s"

#define COPY_STR_FIELD(target, source, field) copy_string_field(&(target.field), source, #field)

static rt_bool_t parse_lives_node(amap_weather_info_t wea_info, cJSON* lives)
{
    if (!wea_info || !lives) {
        LOG_E("wea_info or lives is null!");
        return RT_FALSE;
    }

    LOG_D("parsing lives data...");
    wea_info->type = LIVE_WEATHER;
    cJSON* live_data = cJSON_GetArrayItem(lives, 0);
    copy_string_field(&wea_info->data.live_data.province, live_data, "province");
    copy_string_field(&wea_info->data.live_data.city, live_data, "city");
    copy_string_field(&wea_info->data.live_data.adcode, live_data, "adcode");
    copy_string_field(&wea_info->data.live_data.weather, live_data, "weather");
    copy_string_field(&wea_info->data.live_data.temperature, live_data, "temperature");
    copy_string_field(&wea_info->data.live_data.wind_direction, live_data, "winddirection");
    copy_string_field(&wea_info->data.live_data.wind_power, live_data, "windpower");
    copy_string_field(&wea_info->data.live_data.humidity, live_data, "humidity");
    copy_string_field(&wea_info->data.live_data.report_time, live_data, "reporttime");
    return RT_TRUE;
}

static rt_bool_t parse_forecasts_node(amap_weather_info_t wea_info, cJSON* forecasts)
{
    if (!wea_info || !forecasts) {
        LOG_E("wea_info or forecases is null!");
        return RT_FALSE;
    }

    LOG_D("parsing forecasts data...");
    cJSON* forecasts_obj = cJSON_GetArrayItem(forecasts, 0);
    if (forecasts_obj == RT_NULL) {
        LOG_E("forecasts_obj null!");
        return RT_FALSE;
    }

    wea_info->type = FORECAST_WEATHER;
    copy_string_field(&wea_info->data.forecast_data.city, forecasts_obj, "city");
    copy_string_field(&wea_info->data.forecast_data.adcode, forecasts_obj, "adcode");
    copy_string_field(&wea_info->data.forecast_data.province, forecasts_obj, "province");
    copy_string_field(&wea_info->data.forecast_data.report_time, forecasts_obj, "reporttime");

    cJSON* casts = cJSON_GetObjectItem(forecasts_obj, "casts");
    if (casts) {
        int ncasts = cJSON_GetArraySize(casts);
        wea_info->data.forecast_data.casts = rt_calloc(ncasts, sizeof(struct weather_cast_data));
        wea_info->data.forecast_data.ncasts = ncasts;
        for (int i = 0; i < ncasts; i++) {
            cJSON* casts_item = cJSON_GetArrayItem(casts, i);
            copy_string_field(&wea_info->data.forecast_data.casts[i].date, casts_item, "date");
            copy_string_field(&wea_info->data.forecast_data.casts[i].week, casts_item, "week");
            copy_string_field(&wea_info->data.forecast_data.casts[i].day_weather, casts_item, "dayweather");
            copy_string_field(&wea_info->data.forecast_data.casts[i].night_weather, casts_item, "nightweather");
            copy_string_field(&wea_info->data.forecast_data.casts[i].day_temp, casts_item, "daytemp");
            copy_string_field(&wea_info->data.forecast_data.casts[i].night_temp, casts_item, "nighttemp");
            copy_string_field(&wea_info->data.forecast_data.casts[i].day_wind, casts_item, "daywind");
            copy_string_field(&wea_info->data.forecast_data.casts[i].night_wind, casts_item, "nightwind");
            copy_string_field(&wea_info->data.forecast_data.casts[i].day_power, casts_item, "daypower");
            copy_string_field(&wea_info->data.forecast_data.casts[i].night_power, casts_item, "nightpower");
        }
    }
    return RT_TRUE;
}

amap_weather_info_t amap_weather_get(weather_type_t type)
{
    char* response = RT_NULL;
    size_t resp_len = 0;

    amap_location_info_t loc_info = amap_location_get();
    if (!loc_info) {
        LOG_E("get location loc_info failed!");
        return RT_NULL;
    }

    char* url = rt_malloc(WEATHER_URL_BUFFER_SIZE);
    if (url == RT_NULL) {
        LOG_E("alloc url buffer failed!");
        amap_location_delete(loc_info);
        return RT_NULL;
    }

    // prepare request URL
    const char* extensions = "base";
    if (type == FORECAST_WEATHER) {
        extensions = "all";
    }
    rt_snprintf(url, WEATHER_URL_BUFFER_SIZE, WEATHER_URL_FORMAT, APP_AMAP_API_KEY, loc_info->adcode, extensions);
    amap_location_delete(loc_info);

    // perform HTTP request
    // if (webclient_request(url, RT_NULL, RT_NULL, 0, (void**)&response, &resp_len) < 0) {
    if (http_get(url, &response, &resp_len) < 0) {
        LOG_E("webclient send get request failed.");
        rt_free(url);
        return RT_NULL;
    }
    rt_free(url);
    LOG_D("Got response data length: %d", resp_len);
    for (int i = 0; i < resp_len; i++) {
        char c = response[i];
        rt_kprintf("%c", isprint(c) ? c : '?');
        if (!isprint(c)) {
            rt_kprintf("(%02X)", c);
        }
    }
    rt_kprintf("\n");

    amap_weather_info_t wea_info = RT_NULL;
    if (resp_len > 0) {
        wea_info = rt_calloc(1, sizeof(struct amap_weather_info));
        if (!wea_info) {
            LOG_E("alloc wea_info failed!");
            web_free(response);
            return RT_NULL;
        }

        cJSON* root = cJSON_Parse(response);
        if (root) {
            if (type == LIVE_WEATHER) {
                parse_lives_node(wea_info, cJSON_GetObjectItem(root, "lives"));
            } else if (type == FORECAST_WEATHER) {
                if (cJSON_GetObjectItem(root, "infocode")) {
                    rt_kprintf("infocode: %s\n", cJSON_GetObjectItem(root, "infocode")->valuestring);
                }
                parse_forecasts_node(wea_info, cJSON_GetObjectItem(root, "forecasts"));
            }
            cJSON_Delete(root);
        }
    }

    if (response) {
        web_free(response);
    }
    LOG_D("%s done!", __func__);
    return wea_info;
}

void amap_weather_delete(amap_weather_info_t info)
{
    if (!info) {
        return;
    }
    if (info->type == LIVE_WEATHER) {
        LOG_D("free live weather data...");
        SAFE_FREE(info->data.live_data.adcode);
        SAFE_FREE(info->data.live_data.city);
        SAFE_FREE(info->data.live_data.humidity);
        SAFE_FREE(info->data.live_data.province);
        SAFE_FREE(info->data.live_data.report_time);
        SAFE_FREE(info->data.live_data.temperature);
        SAFE_FREE(info->data.live_data.weather);
        SAFE_FREE(info->data.live_data.wind_direction);
        SAFE_FREE(info->data.live_data.wind_power);
    } else if (info->type == FORECAST_WEATHER) {
        LOG_D("free forecast weather data...");
        SAFE_FREE(info->data.forecast_data.city);
        SAFE_FREE(info->data.forecast_data.adcode);
        SAFE_FREE(info->data.forecast_data.province);
        SAFE_FREE(info->data.forecast_data.report_time);
        for (int i = 0; i < info->data.forecast_data.ncasts; i++) {
            LOG_D("free forecast data %d...", i);
            SAFE_FREE(info->data.forecast_data.casts[i].date);
            SAFE_FREE(info->data.forecast_data.casts[i].week);
            SAFE_FREE(info->data.forecast_data.casts[i].day_weather);
            SAFE_FREE(info->data.forecast_data.casts[i].night_weather);
            SAFE_FREE(info->data.forecast_data.casts[i].day_temp);
            SAFE_FREE(info->data.forecast_data.casts[i].night_temp);
            SAFE_FREE(info->data.forecast_data.casts[i].day_wind);
            SAFE_FREE(info->data.forecast_data.casts[i].night_wind);
            SAFE_FREE(info->data.forecast_data.casts[i].day_power);
            SAFE_FREE(info->data.forecast_data.casts[i].night_power);
        }
    }
    SAFE_FREE(info);
}

static int weather_test(int argc, char* argv[])
{
    if (argc < 2) {
        rt_kprintf("Usage: %s [0|1|2]\n", __func__);
        return -1;
    }

    int opt = argc > 1 ? atoi(argv[1]) : 0;
    amap_weather_info_t info = RT_NULL;

    if (opt == 0) {
        info = amap_weather_get(LIVE_WEATHER);
        RT_ASSERT(info);
        amap_weather_delete(info);
    } else if (opt == 1) {
        info = amap_weather_get(FORECAST_WEATHER);
        RT_ASSERT(info);
        amap_weather_delete(info);
    }
    return 0;
}
MSH_CMD_EXPORT(weather_test, Weather API test);